C进阶 图形化 easyX

概念

easyX的颜色

easyx-image1

easyX中的颜色实际上都是用三原色 红绿蓝 表示出来的

函数RGB(红色部分,绿色部分,蓝色部分);

每个颜色的值都是从0~255

Tips:

使用qq截图 ctrl+alt+a 按c选取颜色

可以直接复制进入RGB函数

easyx-image2

easyX的坐标

坐标原点默认为窗口的左上角,X轴向右为正,Y轴向下为正,度量单位为像素点

easyx-image3

使用的是逻辑坐标 它可以进行修改

setorigin(x,y);                    setaspectratio(1, -1);

将x,y定义为逻辑坐标原点 缩放坐标系 为负时即翻转坐标轴(翻转后字符也会翻)

easyx-image4 easyx-image5

关于窗口

创建

初始化绘图窗口(创建窗口)

easyx-image6
initgraph(800, 600);

创建一个宽800 高600的窗口

注意!这个不是控制台 是图形化界面

如果要显示控制台的话 可以使用

initgraph(800, 600, EX_SHOWCONSOLE);

恶搞使用

initgraph(800, 600, EX_NOCLOSE);//禁用绘图窗口的关闭按钮
initgraph(800, 600, EX_NOMINIMIZE);//禁用绘图窗口的最小化按钮

当然可以加在一起:

initgraph(800, 600,  EX_SHOWCONSOLE | EX_NOCLOSE | EX_NOMINIMIZE);
easyx-image7

关闭

有创建 对应就要有关闭窗口  closegraph();
easyx-image8

避免一闪而过 使用getchar();函数进行一个缓冲 回车后才会执行close指令

背景

大黑框太限制发挥了 这个窗口可以自己设置背景色

setbkcolor(WHITE);//设置背景色
cleardevice();//搭配使用 否则仍为黑

先setbkcolor再cleardevice 组合才能实现换色

一切关于颜色的都可以使用GRB函数      setbkcolor(GRB( , , ,));

模板

在进行操作前先打一套组合拳

#include<easyx.h>
#include<stdio.h>
int main()
{
    initgraph(800, 600);
    setorigin(400, 300);
    //setaspectratio(1, -1);(不建议翻转 因为后续可能需要文字输出)
    setbkcolor(WHITE);
    cleardevice();
    ……
    getchar();
    closegraph();
    return 0;
}

基本绘制图形

easyx-image9

点、线、面——

圆、矩形、椭圆、圆角矩形、扇形、弧形(arc)

多边形

点(putpixel)

easyx-image10

x,y为坐标位置 第三个参数为颜色

绘制2个点

putpixel(0, 0, RED);
putpixel(200, 200, BLUE);

随机绘制1000个点(x - 400~400 y - 300~300)

int x, y;
for (int i = 0; i < 1000; i++)
{
    x = rand() % (800 + 1) - 400;
    y = rand() % (600 + 1) - 300;
    putpixel(x, y, RED);
}
easyx-image11

线(line)

easyx-image12
line(-300, 0, 300, 0);

两点确定一条线 所以给出两个坐标即可

setlinecolor(RED);//默认为白色 会与背景融合 所以设置一下颜色
line(-300, 0, 300, 0);
easyx-image13

圆(circle)

easyx-image14 easyx-image15
setlinecolor(RGB(190, 183, 255));                       同心圆
circle(0, 0, 100);//圆心(x,y),半径
easyx-image16 easyx-image17

矩形(rectangle)

easyx-image18

与截屏同理 从左上角选至右下角即可确定一个矩形

两点就行 不一定要左上到右下

所以rectangle(-200, -100, 200, 100);
easyx-image19

椭圆(ellipse)

easyx-image20

椭圆跟矩形联系密切 是它的内切 所以也是给出两点

rectangle(-200, 100, 200, -100);
ellipse(-200, 100, 200, -100);                       ellipse(-200, 100, 200, -100);
easyx-image21 easyx-image22

圆角矩形(roundrect)

easyx-image23 easyx-image24 easyx-image25

四个角变成了椭圆的弧

前面4个参数仍然是两个点的坐标

后面两个参数则为构造圆角的椭圆的宽与高(简单点就是[x1-x2/2]和[y1-y2]/2)

但是最后两个参数有很多讲究 用好了可以变成像圆但有一部分平的图形 用来当脑袋什么的

扇形(pie)

easyx-image26 easyx-image27 easyx-image28

前四个参数为两点坐标

参数5:起始角弧度

参数6:从起始 顺时针 旋转的一个度数

需要宏定义好

#define PI 3.14

弧形(arc)

easyx-image29 easyx-image30

与pie基本相同 只是arc不会绘制从圆心到圆周的线段

pie(-200, 100, 200, -100, 0, PI / 4);
arc(-200, 100, 200, -100, PI/4, PI /2 );
easyx-image31

多边形(polygon)

easyx-image32

用一个结构体存储想构造的图形的几个顶点

传入这个结构体的首地址(指针) 和 边数

比如:

三角形

POINT points[] = { {0,200},{200,-200},{-200,-200} };
polygon(points, 3);
easyx-image33

四边形

POINT points2[] = { {200,200},{200,-200},{-200,-200},{-200,200 } };
polygon(points2, 4);
easyx-image34

梯形

POINT points3[] = { {100,200},{200,-200},{-200,-200},{-100,200 }};
polygon(points3, 4);
easyx-image35

五边形

double theta = PI / 2;
easyx-image36
double delta = 2 * PI / 5;
int r = 200;
POINT point4[5];
for (int i = 0; i < 5; i++)
{
    point4[i].x = cos(theta + i * delta) * r;
    point4[i].y = sin(theta + i * delta) * r;
}
polygon(point4, 5);

初始角度为90度 循环一次加72度 利用循环得出每个点的x,y

easyx-image37 easyx-image38

Polygon是将每个点依次连接 然后连接首位 (p1~p2~…~p8依次连 然后p8连p1)

如果想描绘非封闭图形 比如字母M

easyx-image39

就需要

不封闭图形(polyline)

POINT point5[5] = { {-400,200},{-200,-200},{0,100},{200,-200},{400,200} };
polyline(point5, 5);
easyx-image40

图形设计

非封闭图形(点线弧…)可以设计它的边框

封闭图形可以设计它的边框以及填充

easyx-image41

在circle、rectangle……前面加上fill或者solid

边框颜色

easyx-image42
setlinecolor(RED);
setlinecolor(RGB(214, 157, 133));

后面的覆盖前面的 由此可以衍生很多颜色的图形

边框样式

一般用这俩参数

easyx-image43 easyx-image44
setlinestyle(PS_DOT);                    setlinestyle(PS_DOT,5);
easyx-image45 easyx-image46

关于自定义边框 详见https://docs.easyx.cn/zh-cn/setlinestyle

easyx-image47

填充颜色

easyx-image48
setfillcolor(YELLOW);

同样能用RGB函数

填充样式

easyx-image49 easyx-image50
setlinecolor(RGB(214, 157, 133));
setlinestyle(PS_DOT);
setfillcolor(RED);
setfillstyle(BS_HATCHED, HS_FDIAGONAL);
POINT points2[] = { {200,200},{200,-200},{-200,-200},{-200,200 } };
fillpolygon(points2, 4);
easyx-image51

自定义填充?

文本输入

绘制文字

outtextxy(int x,int y,文字);

在(x,y)输出文字str

outtextxy(int x,int y,LPCTSTR str);
如outtextxy(10,10,"你好");

注意:使用字符串时出现错误

解决方法:

在字符串前加上L

outtextxy(10,10,L"你好");

2.进入项目 -> 属性 ->配置属性 ->常规 -> 字符集 -> 改为多字节字符集

easyx-image52 easyx-image53

字体颜色

settextcolor(颜色);
如settextcolor(BLUE);
settextcolor(RGB(150, 187, 202));

字体样式

settextstyle(int Height,int Width, 样式);

大小(指定高度、宽度[0为自适应])(也可以理解为文本框的左上角的地址) 字体

settextstyle(30,0,L"行楷");

文字背景

TRANSPARENT透明 OPAQUE,BKMODE_LAST不透明

setbkmode(TRANSPARENT);
settextcolor(RED);                                 settextcolor(RED);
settextstyle(50, 0, L"宋体");                      settextstyle(50, 0, L"宋体");
outtextxy(-130, -30, L"hello easyx");              setbkmode(TRANSPARENT);
outtextxy(-130, -30, L"hello easyx");
easyx-image54 easyx-image55

文本居中于图像

如果一个像素一个像素地调 太麻烦 而且字符一变就不居中了 所以得想办法

需要用到两个函数

获取文本的宽度textwidth(TCHAR c);
获取文本的高度textheight(TCHAR c);

(给出的不是字符串长度 而是像素宽度)

VS 2022需要传入一个TCHAR类型的数组  VS 6.0可以直接传入文本 如textwidth("哈哈哈哈");

给出想看到的文本 使用TCHAR类型的数组 而后传入到text…函数中获取宽、高

TCHAR arr[] = L"居中";

求出字符串长度/2 框的一半长度减字符串的一半长度 就是字符离框的距离

int width = (400-200)/2-textwidth(arr)/2;
int height =(150-50)/2- textheight(arr) / 2;

框的一半长度(y) - 字符串(文本框)的一半长度(x) = n(x轴方向 文本框离图形框的距离)

m(图形框的横坐标)+ n(刚才求出来的) = 文本框的左上角的横坐标

同理

还能求出文本框左上角的纵坐标

知道这个点在哪里 就能实现文字居中于图像

//然后再把框的左上角的x加上这个width y+height  就是字符的居中位置
outtextxy(width + 200, height + 50, arr);

实现:

easyx-image70
setlinecolor(RGB(214, 157, 133));
setlinestyle(PS_DOT);
setfillcolor(RED);
setfillstyle(BS_HATCHED, HS_FDIAGONAL);
fillellipse(-200, -100, 200, 100);
settextcolor(RED);
settextstyle(50, 0, L"宋体");
setbkmode(TRANSPARENT);
TCHAR arr[] = L"hello easyX";
int width = 400 / 2 - textwidth(arr) / 2;
int height = 200 / 2 - textheight(arr) / 2;
outtextxy(width - 200, height - 100, arr);
封装一下
void juzhong(int x, int y, int w, int h)
{
    setbkmode(TRANSPARENT);
    setfillcolor(BROWN);
    //可以随意更换图形 给出左上角的坐标 给出长和宽 就有两个点坐标
    fillellipse (x, y, x + w, y + h);
    //fillroundrect(x, y, x + w, y + h, 10, 10);
    settextstyle(30, 0, L"黑体");
    TCHAR text[] = L"文本";
    int tx = x + ( w - textwidth(text)) / 2;
    int ty = y + ( h - textheight(text))/ 2;
    outtextxy( tx , ty, text);
}

这样一来 任给一个图形的左上角坐标和期望的大小

就能将文字居中在图像内部

因为要从主函数传入想输入的文本 所以得多一个参数 只能用TCHAR类型就很烦

void button(int x, int y, int w, int h, TCHAR* text)
{
    setbkmode(TRANSPARENT);
    setfillcolor(BROWN);
    fillroundrect(x, y, x + w, y + h, 10, 10);
    settextstyle(30, 0, L"黑体");
    TCHAR text_[100];
    for (int i = 0; i < 100; i++)
        text_[i] = text[i];
    int tx = x + ( w - textwidth(text)) / 2;
    int ty = y + ( h - textheight(text))/ 2;
    outtextxy( tx , ty, text);
}

主函数中还要另外这样

TCHAR a[] = L"文本";
button(50, 50, 150, 50,a);

只要给出携带文本的图形大小就OK了(填充、样式在函数里改)

图片输出

定义一个IMAGE类型的变量对象

IMAGE image1;

加载图片loadimage

easyx-image71

最少传入两个参数 ——前面定义的IMAGE类型变量的地址和图片文件地址

有两种方式填入图片文件地址

相对路径

需要把图片保存在CPP文件的同一目录下

./表示当前文件夹下

../表示当前文件夹的父目录下

loadimage(&image1, _T("./cat.jpg"));

一定要用 _T()去进行一个转换 否则报错

绝对路径

使用绝对路径 就不需要将图片要放CPP同一个目录里

复制到的绝对路径是这样的"D:\EasyX代码\图像输出1\图像输出1\cat.jpg"

需要对它进行一点修改

多加一个转义字符/将各目录区分开

loadimage(&image1, _T("D:\\EasyX代码\\图像输出1\\图像输出1\\cat.jpg"));

或者把\改成/

名字有空格的文件夹还要加上双引号 然后前面再加一个\

麻烦 但是有点是无需在同一目录

loadimage(&image1, _T("D:\\easyX素材\\82c0c33265135abb203bb51d9cae4302.jpeg"));

不知道为什么 反正地址的传入都需要使用_T()去进行转换

easyx-image72

在这个函数中还可以加入两个参数

loadimage(&image1, _T("./cat.jpg"), 500, 500);

去定义该图片的宽、高方向上的像素 (这是加载时的步骤 不是输出时的步骤)

输出图片putimage

easyx-image73

三个参数 想把图片输出在哪里?给出它左上角的坐标xy

然后把一开始那个指针(已经经过加载 将图片传入)传进去

putimage(-400, -300, &image1);

鼠标操作

定义好一个按钮 用前面文字居中的函数(封装一下)

easyx-image74

使用两个函数

ExMessage储存鼠标信息

仅需要定义一个该结构的变量就行 它会储存你的鼠标在哪里 进行了什么操作 至于鼠标进行了什么操作该有什么反应的工作交给其他指令

easyx-image75

peekmessage

easyx-image76
peekmessage(&msg, EX_MOUSE);

将存储了鼠标信息的结构体的地址传入 并告知是需要它判断对鼠标的操作

easyx-image77

因为它的返回值是

easyx-image75 easyx-image78

所以与if连用

如果接收到了 怎么样

这是接收与否

那接受的是什么呢 点击?悬挂?右键?

使用switch case去决定哪一种操作要干什么

initgraph(640, 480, EX_SHOWCONSOLE);
TCHAR a[] = L"按钮";
button(50, 50, 150, 50,a);      给出按钮在界面中的位置 左上角(50,50) 宽15050
ExMessage msg;                               定义ExMessage类型的变量msg
while (true){                                无限循环
    if (peekmessage(&msg, EX_MOUSE)){        peekmessage判断是否接收到了鼠标信息
        switch (msg.message){
            case WM_LBUTTONDOWN:                 如果是点击左键 那么输出welcome
            if (msg.x >= 50 && msg.x <= 50 + 150 && msg.y >= 50 && msg.y <= 50 + 50){
                printf("welcome\n");
            }                             当然是要在50,50   200,100这个范围点击
            break;
            case WM_RBUTTONDOWN:             如果是点击右键 那么输出hello
            if (msg.x >= 50 && msg.x <= 50 + 150 && msg.y >= 50 && msg.y <= 50 + 50){
                printf("hello\n");
            }
            break;
        }
    }
}

注意!

ExMessage给鼠标传的坐标是物理坐标而不是逻辑坐标

easyx-image79 easyx-image80

如果使用了setorigin定义逻辑坐标点

我们的图形是根据这个逻辑坐标确定位置的

但是 鼠标的位置不由逻辑坐标的改变而改变 而是根据物理坐标定位

所以不能简单的使用图形框的左上角坐标去加上宽高来确定鼠标点击的区域

也可以使用outtext函数实现在图形化界面输出

easyx-image81

还可以使用输出鼠标当前所在坐标的形式

验证之前 鼠标所使用的是物理坐标的结论

ExMessage msg;
while (true)
{
    if (peekmessage(&msg, EX_MOUSE))
    {
        printf("坐标(%d,%d)\n", msg.x, msg.y);
    }
}
easyx-image82

(去定义一个逻辑坐标点 对比两种情况下的鼠标位置的坐标变化 会发现跟逻辑坐标在哪没关系)

那么是不是可以通过这种方式来去确定想知道的点的坐标

来帮助绘图

这样的话就一定要省去定义逻辑坐标的步骤 不然搞得晕头转向的

ExMessage msg;
while (true){
    if (peekmessage(&msg, EX_MOUSE)){
        switch (msg.message){
            case WM_RBUTTONDOWN:
            if (msg.x >= 0 && msg.x <= 1000 && msg.y >= 0 && msg.y <= 650){
                printf("坐标(%d,%d)\n", msg.x, msg.y);
            }
easyx-image83
break;
}
}
}

逻辑坐标的范围0,0到多少都行

不过最好用自己定义的页面大小

键盘操作

两种方法

getch()

需要使用头文件 conio.h

GetAsyncKeyState(键值);

需要使用头文件windows.h 但EasyX里包含了

easyx-image84

_getch();

需要使用返回值来判断

字符那边使用的是ASCII码比较

while(1)
{
    char key = _getch();
    printf("%d,%c\n",key,key);
}

得到的全是a~z的ASCII码值

如果是按上下左右键

easyx-image85 easyx-image86 easyx-image87

返回的则是虚拟键值

上:72 下:80 左:75 右:77

所以 可以使用switch语句 设置上下左右键→ →

在循环外加上x,y的初始化

创建一个圆 坐标为xy 半径为20

在按下w/W/↑时y--

……

(注意 向上y是减减 因为y轴朝下)

得到这段代码 可以控制球的移动

int x=0, y = 0;
while(1)
{
    setfillcolor(BROWN);
easyx-image88
fillcircle(x, y, 20);
char key = _getch();
printf("%d,%c\n",key,key);
switch (key)
{
    case 72:
    case 'w':
    case 'W':
    y--;
    printf("上键\n");
    break;
    case 80:
    case 's':
    case 'S':
    y++;
    printf("下键\n");
    break;
    case 75:
    case 'a':
    case 'A':
    x--;
    printf("左键\n");
    break;
    case 77:
    case 'd':
    case 'D':
    x++;
    printf("右键\n");
    break;
}
}
觉得移动太慢可以修改每按一次x,y的改变值  比如x+=5;

这样是会留有球的运动轨迹的(因为没有清屏)

用这个特性是不是还可以把球的位置定义成鼠标返回的xy值 然后跟随鼠标移动而绘图

一个简易绘图板就出来了

问题在于不能抬笔 只能一笔画 如果要实现 那就使用如果点击左键就画画

要更像一点画图板的话 就要加上清屏功能

ExMessage msg;
while (true)
easyx-image89
{
    if (peekmessage(&msg, EX_MOUSE))
    {
        setfillcolor(BROWN);
        fillcircle(msg.x,msg. y, 5);
    }
}
easyx-image90

或者这样 判断鼠标移动信息

ExMessage msg;
while (true)
{
    if (peekmessage(&msg, EX_MOUSE))
    {
        switch (msg.message)
        {
            case WM_MOUSEMOVE:
            setfillcolor(BROWN);
            fillcircle(msg.x, msg.y, 5);
        }
    }
}

想实现长按左键画图

但失败了

case WM_LBUTTONDOWN && WM_MOUSEMOVE:
setfillcolor(BROWN);
fillcircle(msg.x, msg.y, 5);

布尔运算符|不行 逻辑运算符&&也不行

跑偏了

那么怎么实现不留球的运动轨迹

cleardevice();
easyx-image91

本来尝试使用clearcircle函数

(生成的是一个圆 每次都清除上一个生成的圆 但是不知道为什么失败了)

easyx-image92

可能是位置问题 原来……此清除圆非彼清除圆

easyx-image93 easyx-image94

等等 它删除的是圆内的东西

留下来边框

那如果是无边框填充的图形呢

生成一个圆显示完后就立刻删除

成功实现

所以清除一个圆的轨迹有两种方法

一、清除全屏

int x=0, y = 0;
while(1)
{
    cleardevice();
    setfillcolor(BROWN);
    solidcircle(x, y, 20);
    char key = _getch();
    printf("%d,%c\n",key,key);
    switch (key)
    {……

清除该圆区域

int x=0, y = 0;
while(1)
{
    setfillcolor(BROWN);
    solidcircle(x, y, 20);
    char key = _getch();
    clearcircle(x, y, 20);
    printf("%d,%c\n",key,key);
    switch (key)
    {……

对于第一种就存在一个问题 如果在while循环外设置的背景也会一起被清除

要避免这种情况 就需要把所有东西放进while循环

第二种不确定圆区域内的背景会不会被清除?如果会那么跟第一种就没啥区别

好吧 会 那么 又衍生出一种玩法:把这个图片“吃掉”!

easyx-image95

老老实实把所有东西都放入循环 清屏下面

int x = 0, y = 0;
while (1)
{
    cleardevice();
    IMAGE image1;
    loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg"));
    putimage(0, 0, &image1);
easyx-image96
setfillcolor(BROWN);
solidcircle(x, y, 20);
char key = _getch();
//clearcircle(x, y, 20);
printf("%d,%c\n", key, key);
switch (key)
{……

又一个问题出来了

我们会发现在这个过程中

图片会一闪一闪

明显卡顿

如果仅清除一个区域 卡顿的就不是图片 只有球一闪一闪了

easyx-image97

怎么解决?

三个函数:

BeginBatchDraw();开始批量绘图
FlushBatchDraw();
EndBatchDraw();结束批量绘制
easyx-image98

之前防止清屏是将所有步骤都放入循环

在这个循环里不断绘制 不断清屏 循环往复

我们每个过程都看到了 所以会产生卡顿

那么这几个函数

就是在内存里存在一个缓冲区

提前帮我们绘制好 在需要时直接调用出来(所谓批量绘图)

这样我们就能直接看到图片显示 而看不到它绘制的过程

所以就解决了卡顿的问题

easyx-image99
#include<easyx.h>
#include<stdio.h>
#include<conio.h>
int main()
{
    initgraph(800, 600, EX_SHOWCONSOLE);
    BeginBatchDraw();
    int x = 0, y = 0;
    while (1)
    {
        cleardevice();
        IMAGE image1;
        loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg"));
        putimage(0, 0, &image1);
        setfillcolor(BROWN);
        solidcircle(x, y, 20);
        FlushBatchDraw();
        char key = _getch();
        //clearcircle(x, y, 20);
        printf("%d,%c\n", key, key);
        switch (key)
        {
            case 72:case 'w':case 'W':
            y -= 3;printf("上键\n");break;
            case 80:case 's':case 'S':
            y += 3;printf("下键\n");break;
            case 75:case 'a':case 'A':
            x -= 3;printf("左键\n");break;
            case 77:case 'd':case 'D':
            x += 3;printf("右键\n");break;
        }
    }
    EndBatchDraw();
    getchar();
    closegraph();
    return 0;
}

GetAsyncKeyState(键值);

easyx-image183
initgraph(800, 600, EX_SHOWCONSOLE);
BeginBatchDraw();
int x = 0, y = 0;
while (1)
{
    cleardevice();
    IMAGE image1;
    loadimage(&image1, _T("D:\\easyX素材\\fb517c4617291f66e1809b007ad9c430.jpeg"));
    putimage(0, 0, &image1);
    setfillcolor(BROWN);
    solidcircle(x, y, 20);
    FlushBatchDraw();
    if (GetAsyncKeyState(VK_UP))
    {
        y -= 1;
    }
    if (GetAsyncKeyState(VK_DOWN))
    {
        y += 1;
    }
    if (GetAsyncKeyState(VK_LEFT))
    {
        x -= 1;
    }
    if (GetAsyncKeyState(VK_RIGHT))
    {
        x += 1;
    }
}
EndBatchDraw();
getchar();
closegraph();

这种方法比较丝滑 灵敏度高 但是只能使用↑↓←→

D:\EasyX代码\键盘操作\键盘操作\源.cpp

音乐播放

首先要用到两个头文件

#include<Windows.h>
#include<mmsystem.h>//包含多媒体设备接口头文件

或者#include<easyx.h>

(easyx.h里包含了这两个 如果用了easyx就可以不用包含)

还需要加载静态库winmm.lib

#pragma comment (lib,"Winmm.lib")//加载静态库

接着使用mciSendString函数进行打开、播放、重复播放、关闭操作

easyx-image184 easyx-image185 easyx-image186

alias取别名

如果使用的Unicode字符集

相对路径:

从本级文件夹打开音乐

mciSendString(L"open ./BGM2.mp3 alias a", NULL, 0, NULL);

播放音乐

mciSendString(L"play a", NULL, 0, NULL);

重复播放

mciSendString(L"repeat a", NULL, 0, NULL);
mciSendString(L"play a repeat", NULL, 0, NULL);

关闭音乐

mciSendString(L"close a", NULL, 0, NULL);//记得用if条件 不然一打开就关闭了

绝对路径:

mciSendString(L"open D:\\easyX素材\\BGM1.mp3 alias a", NULL, 0, NULL);
mciSendString(L"play a", NULL, 0, NULL);
mciSendString(L"repeat a", NULL, 0, NULL);
mciSendString(L"play a repeat", NULL, 0, NULL);
mciSendString(L"close a", NULL, 0, NULL);

D:\EasyX代码\音乐\音乐\源.cpp

使用多字符集就无需L

easyx-image52 easyx-image53

利用字符处理函数

进行一个封装

可在任何地方加PlayMusic(地址)会结束前一首歌 而播放下一首 可用于场景切换

#include<conio.h>
#include<Windows.h>
#include<mmsystem.h>//包含多媒体设备接口头文件

#pragma comment (lib,"Winmm.lib")//加载静态库

void PlayMusic(const char* FileName)
{
    char cmdString[50] = "open ";
    strcat_s(cmdString, FileName);
    strcat_s(cmdString, " alias bkmusic");
    mciSendString("close bkmusic", NULL, 0, NULL);//把上一次的音乐关掉
    mciSendString(cmdString, NULL, 0, NULL);//打开音乐
    mciSendString("play bkmusic", NULL, 0, NULL);//播放一次音乐
}
int main()
{
    PlayMusic("D:\\easyX素材\\BGM1.mp3");     //仅需给出地址 多加一个\
    _getch();
    return 0;
}

D:\EasyX代码\音乐2\音乐2\源.cpp

引用的音乐文件名最好改成中文的 而且不能有空格

格式方面:使用qq音乐的音频转码去改成MP3格式

easyx-image187 easyx-image188

修改窗口标题以及弹窗

窗口标题

GetHWnd();获取窗口句柄 获取之后用于操作窗口

定义一个HWND类型的变量存储获取到的句柄

HWND hnd = GetHWnd();

可以对我们的图形化界面去进行操作了

SetWindowText(hnd, L"hello easyx");

hnd是我们传入的HWND类型变量 后方是想设置的图形化窗口标题

SetWindowText函数包含在window.h里 但是easyx包含了windows.h

没用图形化界面的话也用不着修改它的名字 所以这个无所谓

图形化窗口可以改 其实控制台的窗口的名字也能改

System(“title 任取名字”)

system("title 我是控制台");
easyx-image189

养成习惯封装

void change()
{
    system("title 我是控制台");//修改控制台名字
    //获取窗口句柄
    HWND hnd = GetHWnd();
    SetWindowText(hnd, L"hello easyx");
}

弹窗

MessageBox(1,2,3,4);
easyx-image190

四个参数:

窗口句柄(可缺省)

提示框里面的东西

提示框标题

按钮(必须要有)

MessageBox(NULL, L"文本内容", L"标题", MB_OKCANCEL);
MessageBox(hnd, L"文本内容", L"标题", MB_YESNO);

区别在于

如果写上了窗口句柄 就必须先点击这个弹窗才能进行图形界面的其他操作

easyx-image191

长这样 按钮是不可缺省的

包括

MB_OKMB_OKCANCELMB_ABORTRETRYIGNOREMB_YESNOCANCEL MB_YESNO MB_RETRYCANCEL
easyx-image192

MB_OK

easyx-image193

MB_OKCANCEL

easyx-image194

MB_ABORTRETRYIGNORE

easyx-image195

MB_YESNOCANCEL

easyx-image196

MB_YESNO

easyx-image197

MB_RETRYCANCEL

easyx-image198

但是按了不同按钮应该会有后续不同操作吧

所以就需要if去判断

怎么判断?

easyx-image199 easyx-image200

打开MessageBox函数的定义

发现它的返回值是一个int

那么就可以定义一个int类型去存储它

int n = MessageBox(hnd, L"文本内容", L"标题", MB_OKCANCEL);

那跟什么判断呢?因为不知道返回值是什么

直接去与宏比较

if (n == IDYES)//不知道返回了什么 那就直接和宏比
{
    printf("你点击了确定\n");
}
if (n == IDNO)
{
    printf("你点击了取消\n");
}

比较对象是和MessageBox里第四个参数有关的

如果用了MB_OKCANCEL 就与IDOK 和ODCANCEL比较

easyx-image201
#include<easyx.h>
#include<stdio.h>
void change()
{
    system("title 我是控制台");
    HWND hnd = GetHWnd();
    SetWindowText(hnd, L"hello easyx");/
    int n = MessageBox(hnd, L"文本内容", L"标题", MB_OKCANCEL);
    if (n == IDYES){
        printf("你点击了确定\n");
    }
    if (n == IDNO)
    {
        printf("你点击了取消\n");
    }
}
int main()
{
    initgraph(800, 600,EX_SHOWCONSOLE);
    change();
    getchar();
    closegraph();
    return 0;
}

D:\EasyX代码\修改窗口标题\修改窗口标题\源.cpp

贪吃蛇

构建好框架 留下死循环以便绘图

#include<stdio.h>
#include<easyx.h>
int main()
{
    initgraph(640, 480);
    setbkcolor(RGB(206, 212, 240));
    cleardevice();
    while (true)
    {
    }
    getchar();
    closegraph();
    return 0;
}

想定义蛇 应该有一个结构体类型存储它

要有坐标coor 蛇的节数size 移动方向dir 移动速度speed 以及分数

#define SNAKE_NUM 500
struct Snake
{
int size;
int dir;
int speed;
POINT coor[SNAKE_NUM];
};
easyx-image202

Point是一个已经定义好了的结构体 内含x,y

因为蛇的每一节都应该有一个坐标 所以得用结构体数组去存储每一节的坐标

又因为不知道蛇到底有多长

所以先用宏定义 把长度设置为500 不够的话改一下宏定义就好了

数据初始化 让蛇一开始就有三节 数组0的方向为蛇头 后面加蛇身更方便

void GameInit()
{
    snake.size = 3;
easyx-image203
snake.speed = 10;
snake.dir;                //先不管
for (int i = snake.size - 1; i >= 0; i--)
{
    snake.coor[i].x = 40-10 * i;
    snake.coor[i].y = 10;
}
}

再采用封装的方式 把主函数中的窗口绘制也放入函数当中去

后面考虑到方向 所以这里把初始化方向设置为向右

void GameInit()
{
    initgraph(640, 480);
    snake.size = 3;
    snake.speed = 10;
    snake.dir=RIGHT;
    for (int i = snake.size - 1; i >= 0; i--)
    {
        snake.coor[i].x = 40-10 * i;
        snake.coor[i].y = 10;
    }
}
void GameDraw()
{
    //设置背景颜色
    setbkcolor(RGB(206, 212, 240));
    cleardevice();
    //绘制蛇   三节、每节多长 有坐标 再加上半径就行了
    for (int i = 0; i < snake.size; i++)
    {
        solidcircle(snake.coor[i].x, snake.coor[i].y, 5);
    }
}
easyx-image204

蛇的移动

防止卡顿就需要用到双缓冲绘图

BeginBatchDraw();
FlushBatchDraw();
EndBatchDraw();

绘图函数全放入之前的while死循环中去

避免移动太快可以使用Sleep(300); 让它延迟300毫秒

移动一个很简单 怎么让所有的一起动?

同初始化一样使用for循环一起移动吗?

但是还有一个dir方向问题

如果使用了for循环 让它向下 那么三个都会一起平移向下 没有调头的感觉

想要的效果应该是 调头之后 后面的依次补上前一个的位置

所以 跟着这个思路 后面的coor等于前面的coor

先使用枚举 搞出方向方便一会switch用

enum DIR //蛇的方向
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
};

再把简单的识别键盘操作的函数写了 在向右时肯定不能向左 所以加个判断

void KeyControl()
{
    if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN)
    {
        snake.dir = UP;
    }
    if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
    {
        snake.dir = DOWN;
    }
    if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
    {
        snake.dir = LEFT;
    }
    if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
    {
        snake.dir = RIGHT;
    }
}

且不管蛇身如何移动 蛇头移动

switch (snake.dir)
{
    case UP:
    snake.coor[0].y-=snake.speed;
    break;
    case DOWN:
    snake.coor[0].y+=snake.speed;
    break;
    case LEFT:
    snake.coor[0].x-=snake.speed;
    break;
    case RIGHT:
    snake.coor[0].x+=snake.speed;
    break;
}

蛇身跟随头移动

从尾巴开始 一个接一个替代前一个的位置 头部是在不断移动的

如果从头部开始 后一个替代前一个就可能重叠

for (int i = snake.size - 1; i > 0; i--)
    snake.coor[i] = snake.coor[i - 1];

顺序换一下 因为一开始初始化了 蛇头一直在向右移动 尾巴应该先跟上

void SnakeMove(){
    //蛇身跟随头移动
    for (int i = snake.size-1; i >0; i--){
        snake.coor[i] = snake.coor[i - 1];
    }
    //蛇头移动
    switch (snake.dir){
        case UP:
        snake.coor[0].y-=snake.speed;
        break;
        case DOWN:
        snake.coor[0].y+=snake.speed;
        break;
        case LEFT:
        snake.coor[0].x-=snake.speed;
        break;
        case RIGHT:
        snake.coor[0].x+=snake.speed;
        break;
    }
}

移动上没问题了 但是出现了一个跑出屏幕的问题

两种解决方案

1、让它从屏幕另一边出来 2、撞墙游戏结束

怎么让它从屏幕另一边出来

在蛇移动的时候加一个判断 如果蛇头的y<0了 那么意味着跑上面去了

让它从下面冒出来的话 x轴的数值是不用变的 把y=0变成y=窗口高度就行了

easyx-image205

为了可变性 所以跟蛇身长度一样 使用宏定义 去把窗口的宽、高写在前面

#define Win_width 640
#define Win_height 480
initgraph(Win_width, Win_height);

再严谨点 其实圆心加半径(y+半径小于0时)就已经出去了

#define SNAKE_BODY 5

初始化蛇的函数 把原来的5 改成SNAKE_BODY

for (int i = 0; i < snake.size; i++)
{
    solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY);
}

蛇移动的函数里 每个方向上都加上判断

if (snake.coor[0].y+ SNAKE_BODY <= 0)
{
    snake.coor[0].y = Win_height;
}
easyx-image206

OK 以此类推

//移动蛇
void SnakeMove()
{
    //蛇身跟随头移动
    for (int i = snake.size-1; i >0; i--)
    {
        snake.coor[i] = snake.coor[i - 1];
    }
    //蛇头移动
    switch (snake.dir)
    {
        case UP:
        snake.coor[0].y-=snake.speed;
        if (snake.coor[0].y+ SNAKE_BODY <= 0)
        {
            snake.coor[0].y = Win_height;
        }
        break;
        case DOWN:
        snake.coor[0].y+=snake.speed;
        if (snake.coor[0].y + SNAKE_BODY >= Win_height)
        {
            snake.coor[0].y = 0;
        }
        break;
        case LEFT:
        snake.coor[0].x-=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY <= 0)
        {
            snake.coor[0].x = Win_width;
        }
        break;
        case RIGHT:
        snake.coor[0].x+=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY >= Win_width)
        {
            snake.coor[0].x = 0;
        }
        break;
    }

实现出界跳转

蛇的初始化 移动 转弯 越界问题都解决了 接下来就是生长的问题了

生长肯定是要吃到食物

所以得再定义一个食物类型

得有坐标 大小 颜色 以及是否被吃

//定义食物结构
struct Food
{
    int x;
    int y;
    int r;
    bool flag;//布尔类型 只有0/1 可以用来判断是否被吃
    DWORD color;
}food;

在初始化里面 因为食物应该是随机出现在屏幕上的 所以使用rand()

让rand()%窗口宽 那么它一定为0~窗口宽的范围内

同理

food.x = rand() % Win_width;
food.y = rand() % Win_height;

但这样不够 如果不设置随机数种子 那么每次产生的都是固定的整数

而设置随机数种子需要头文件#include<stdlib.h>

srand();

一般将时间当做种子 因为时间是在不断变化的

srand(GetTickCount());
GetTickCount();是获取系统开机 到现在所经过的毫秒数 把它返回给srand 那就是随机种子了

颜色这里也可以随机产生 这就很巧妙

RGB是获取三原色的调色嘛 红绿蓝各自在0~255

那么如果想要全部的颜色就把随机数%上256 余数就落在0~255里面了

food.color = RGB(rand() % 256, rand() % 256, rand() % 256);

大小也能随机 那就是2~5

food.r = rand() % 3 + 2;

初始化的时候肯定没被吃嘛

food.flag = true;

那么接下来就是绘制食物

//绘制食物
if (food.flag)
{
    solidcircle(food.x, food.y, food.r);
}

如何判断吃到与否?

easyx-image207

四种情况 分别从上下左右四个方向吃

可以用相切判断 但会显得判断语句非常长

且把蛇头的圆心 触碰到食物的边界(4个点)当做吃到食物

void Eatfood()
{
    if (snake.coor[0].x >= food.x - food.r
    && snake.coor[0].x <= food.x + food.r
    && snake.coor[0].y >= food.y - food.r
    && snake.coor[0].y <= food.y + food.r
    && food.flag == 1)
    {
        ……
    }
}

如果吃到了 那么 蛇身应该变长1

食物应该消失

snake.size++;
food.flag = 0;

食物消失了应该再生成一个

if (food.flag == 0)
{
    food.x = rand() % Win_width;
    food.y = rand() % Win_height;
    food.flag = 1;
    food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
    food.r = rand() % 4 + 3;
}

所以:

#include<stdio.h>
#include<easyx.h>
#include<stdlib.h>
#define Win_width 640
#define Win_height 480
#define SNAKE_BODY 5
#define SNAKE_NUM 500
enum DIR //蛇的方向
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
};
//定义蛇结构
struct Snake
{
    int size;
    int dir;
    int speed;
    POINT coor[SNAKE_NUM];
}snake;
//定义食物结构
struct Food
{
    int x;
    int y;
    int r;
    bool flag;//布尔类型 只有0/1 可以用来判断是否被吃
    DWORD color;
}food;
//数据初始化
void GameInit()
{
    //初始化图像窗口
    initgraph(Win_width, Win_height);
    //设置随机数种子 获取开机时间到现在的毫秒数 返回当种子
    srand(GetTickCount());
    //初始化蛇 三节 向右 速度为10
    snake.size = 3;
    snake.speed = 10;
    snake.dir=RIGHT;
    for (int i = snake.size - 1; i >= 0; i--)
    {
        snake.coor[i].x = 40-10 * i;
        snake.coor[i].y = 10;
    }
    //初始化食物
    food.x = rand() % Win_width;
    food.y = rand() % Win_height;
    food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
    food.r = rand() % 4 + 3;
    food.flag = 1;
}
void GameDraw()
{
    //设置背景颜色
    setbkcolor(RGB(206, 212, 240));
    cleardevice();
    //绘制蛇
    for (int i = 0; i < snake.size; i++)
    {
        solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY);
    }
    //绘制食物
    if (food.flag==1)
    {
        setfillcolor(food.color);
        solidcircle(food.x, food.y, food.r);
    }
}
//移动蛇
void SnakeMove(){
    //蛇身跟随头移动
    for (int i = snake.size-1; i >0; i--){
        snake.coor[i] = snake.coor[i - 1];
    }
    //蛇头移动
    switch (snake.dir)
    {
        case UP:
        snake.coor[0].y-=snake.speed;
        if (snake.coor[0].y+ SNAKE_BODY <= 0)
        {
            snake.coor[0].y = Win_height;
        }
        break;
        case DOWN:
        snake.coor[0].y+=snake.speed;
        if (snake.coor[0].y + SNAKE_BODY >= Win_height)
        {
            snake.coor[0].y = 0;
        }
        break;
        case LEFT:
        snake.coor[0].x-=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY <= 0)
        {
            snake.coor[0].x = Win_width;
        }
        break;
        case RIGHT:
        snake.coor[0].x+=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY >= Win_width)
        {
            snake.coor[0].x = 0;
        }
        break;
    }
}
void KeyControl()
{
    if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN)
    {
        snake.dir = UP;
    }
    if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
    {
        snake.dir = DOWN;
    }
    if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
    {
        snake.dir = LEFT;
    }
    if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
    {
        snake.dir = RIGHT;
    }
}
void Eatfood()
{
    if (snake.coor[0].x >= food.x - food.r
    && snake.coor[0].x <= food.x + food.r
    && snake.coor[0].y >= food.y - food.r
    && snake.coor[0].y <= food.y + food.r
    && food.flag == 1)
    {
        snake.size++;
        food.flag = 0;
    }
    if (food.flag == 0)
    {
        food.x = rand() % Win_width;
        food.y = rand() % Win_height;
        food.flag = 1;
        food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
        food.r = rand() % 4 + 3;
    }
}
int main()
{
    GameInit();
    BeginBatchDraw();
    while (true){
        GameDraw();
        SnakeMove();
        FlushBatchDraw();
        KeyControl();
        Eatfood();
        Sleep(240);
    }
    EndBatchDraw();
    getchar();
    closegraph();
    return 0;
}

这样一来 基本功能就都具备了

最终版

#include<stdio.h>
#include<easyx.h>
#include<conio.h>
#include<stdlib.h>
#include<mmsystem.h>

#pragma comment (lib,"Winmm.lib")

#define Win_width 640
#define Win_height 480
#define SNAKE_BODY 5
#define SNAKE_NUM 500
void PlayMusic(const char* FileName)
{
    char cmdString[50] = "open ";
    strcat_s(cmdString, FileName);
    strcat_s(cmdString, " alias bkmusic");
    mciSendString("close bkmusic", NULL, 0, NULL);//把上一次的音乐关掉
    mciSendString(cmdString, NULL, 0, NULL);//打开音乐
    mciSendString("play bkmusic repeat", NULL, 0, NULL);//播放音乐
}
void change()
{
    HWND hnd = GetHWnd();
    SetWindowText(hnd, "纯享版贪吃蛇W");
}
enum DIR //蛇的方向
{
    UP,
    DOWN,
    LEFT,
    RIGHT,
};
//定义蛇结构
struct Snake
{
    int size;
    int dir;
    int speed;
    POINT coor[SNAKE_NUM];
    int score;
}snake;
//定义食物结构
struct Food
{
    int x;
    int y;
    int r;
    bool flag;//布尔类型 只有0/1 可以用来判断是否被吃
    DWORD color;
}food;
//数据初始化
void GameInit()
{
    //初始化图像窗口
    initgraph(Win_width, Win_height);
    //设置随机数种子 获取开机时间到现在的毫秒数 返回当种子
    srand(GetTickCount());
    //初始化蛇 三节 向右 速度为10
    snake.size = 3;
    snake.speed = 10;
    snake.dir=RIGHT;
    snake.score = 0;
    for (int i = snake.size - 1; i >= 0; i--)
    {
        snake.coor[i].x = 40-10 * i;
        snake.coor[i].y = 10;
    }
    //初始化食物
    food.x = rand() % (Win_width-10);
    food.y = rand() % (Win_height-10);
    food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
    food.r = 5;
    food.flag = 1;
}
void GameDraw()
{
    //设置背景颜色
    setbkcolor(RGB(206, 212, 240));
    cleardevice();
    //绘制蛇
    for (int i = 0; i < snake.size; i++)
    {
        solidcircle(snake.coor[i].x, snake.coor[i].y, SNAKE_BODY);
    }
    //绘制食物
    if (food.flag==1)
    {
        setfillcolor(food.color);
        solidcircle(food.x, food.y, food.r);
    }
    //显示分数
    char temp[20] = "";
    sprintf(temp, "分数:%d", snake.score);
    setbkmode(TRANSPARENT);
    outtextxy(20, 20, temp);
}
//移动蛇
void SnakeMove()
{
    //蛇身跟随头移动
    for (int i = snake.size-1; i >0; i--)
    {
        snake.coor[i] = snake.coor[i - 1];
    }
    //蛇头移动
    switch (snake.dir)
    {
        case UP:
        snake.coor[0].y-=snake.speed;
        if (snake.coor[0].y+ SNAKE_BODY <= 0)
        {
            snake.coor[0].y = Win_height;
        }
        break;
        case DOWN:
        snake.coor[0].y+=snake.speed;
        if (snake.coor[0].y + SNAKE_BODY >= Win_height)
        {
            snake.coor[0].y = 0;
        }
        break;
        case LEFT:
        snake.coor[0].x-=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY <= 0)
        {
            snake.coor[0].x = Win_width;
        }
        break;
        case RIGHT:
        snake.coor[0].x+=snake.speed;
        if (snake.coor[0].x + SNAKE_BODY >= Win_width)
        {
            snake.coor[0].x = 0;
        }
        break;
    }
}
void KeyControl()
{
    if (GetAsyncKeyState(VK_UP) && snake.dir != DOWN)
    {
        snake.dir = UP;
    }
    if (GetAsyncKeyState(VK_DOWN) && snake.dir != UP)
    {
        snake.dir = DOWN;
    }
    if (GetAsyncKeyState(VK_LEFT) && snake.dir != RIGHT)
    {
        snake.dir = LEFT;
    }
    if (GetAsyncKeyState(VK_RIGHT) && snake.dir != LEFT)
    {
        snake.dir = RIGHT;
    }
}
void Eatfood()
{
    if (snake.coor[0].x >= food.x - food.r
    && snake.coor[0].x <= food.x + food.r
    && snake.coor[0].y >= food.y - food.r
    && snake.coor[0].y <= food.y + food.r
    && food.flag == 1)
    {
        snake.size++;
        snake.score += 10;
        food.flag = 0;
    }
    if (food.flag == 0)
    {
        food.x = rand() % Win_width;
        food.y = rand() % Win_height;
        food.flag = 1;
        food.color = RGB(rand() % 256, rand() % 256, rand() % 256);
        food.r = 5;
    }
}
int main()
{
    GameInit();
    PlayMusic("D:\\easyX素材\\BGM4.mp3");
    change();
    BeginBatchDraw();
    while (true)
    {
        GameDraw();
        SnakeMove();
        FlushBatchDraw();
        Eatfood();
        KeyControl();
        Sleep(220);
    }
    EndBatchDraw();
    getchar();
    closegraph();
    return 0;
}

井字棋

#include<stdio.h>
#include<easyx.h>
#define BKWIDTH 300
#define BKHEIGHT 300
#define PICWIDTH BKWIDTH/3
#define PICHETGHT BKHEIGHT/3
int arr[3][3];
void InitDraw()
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            arr[i][j] = -1;
        }
    }
    initgraph(BKWIDTH, BKHEIGHT);
    setbkcolor(RGB(195, 195, 195));
    cleardevice();
    setlinecolor(RGB(237, 28, 36));
    setlinestyle(PS_SOLID | PS_JOIN_BEVEL,1);
    line(0, 0, BKWIDTH, 0);
    line(0, PICHETGHT, BKWIDTH, PICHETGHT);
    line(0, 2*PICHETGHT, BKWIDTH, 2 * PICHETGHT);
    line(0, BKHEIGHT, BKWIDTH, BKHEIGHT);
    line(0, 0, 0, BKHEIGHT);
    line(PICWIDTH, 0, PICWIDTH, BKHEIGHT);
    line(2*PICWIDTH, 0, 2 * PICWIDTH, BKHEIGHT);
    line(BKWIDTH, 0, BKWIDTH, BKHEIGHT);
}
void DrawHook(int x, int y)
{
    IMAGE hook;
    loadimage(&hook, "D:\\easyX素材\\勾.png",PICWIDTH-1,PICHETGHT-1);
    putimage(x+1, y+1, &hook);
}
void DrawFork(int x, int y)
{
    IMAGE fork;
    loadimage(&fork, "D:\\easyX素材\\叉.png", PICWIDTH - 1, PICHETGHT - 1);
    putimage(x + 1, y + 1, &fork);
}
int Region1(short x,short y)
{
    if (x >= 0 && x <= BKWIDTH / 3 && y>=0 && y<=BKHEIGHT/3)
        return 1;
    else
        return 0;
}
int Region2(short x, short y)
{
    if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= 0 && y <= PICHETGHT)
        return 1;
    else
        return 0;
}
int Region3(short x, short y)
{
    if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= 0 && y <= PICHETGHT)
        return 1;
    else
        return 0;
}
int Region4(short x, short y)
{
    if (x >= 0 && x <= PICWIDTH && y >= PICHETGHT && y <= 2*PICHETGHT)
        return 1;
    else
        return 0;
}
int Region5(short x, short y)
{
    if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= PICHETGHT && y <= 2 * PICHETGHT)
        return 1;
    else
        return 0;
}
int Region6(short x, short y)
{
    if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= PICHETGHT && y <= 2 * PICHETGHT)
        return 1;
    else
        return 0;
}
int Region7(short x, short y)
{
    if (x >= 0 && x <= PICWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT)
        return 1;
    else
        return 0;
}
int Region8(short x, short y)
{
    if (x >= PICWIDTH && x <= 2*PICWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT)
        return 1;
    else
        return 0;
}
int Region9(short x, short y)
{
    if (x >= 2*PICWIDTH && x <= BKWIDTH && y >= 2*PICHETGHT && y <= BKHEIGHT)
        return 1;
    else
        return 0;
}
void Play()
{
    ExMessage msg;
    if (peekmessage(&msg, EX_MOUSE))
    {
        switch (msg.message)
        {
            case WM_LBUTTONDOWN:
            if (Region1(msg.x, msg.y))
            {
                DrawHook(0, 0);
                arr[0][0] = { 1 };
            }
            if (Region2(msg.x, msg.y))
            {
                DrawHook(PICWIDTH, 0);
                arr[0][1] = { 1 };
            }
            if (Region3(msg.x, msg.y))
            {
                DrawHook(2 * PICWIDTH, 0);
                arr[0][2] = { 1 };
            }
            if (Region4(msg.x, msg.y))
            {
                DrawHook(0, PICHETGHT);
                arr[1][0] = { 1 };
            }
            if (Region5(msg.x, msg.y))
            {
                DrawHook(PICWIDTH, PICHETGHT);
                arr[1][1] = { 1 };
            }
            if (Region6(msg.x, msg.y))
            {
                DrawHook(2 * PICWIDTH, PICHETGHT);
                arr[1][2] = { 1 };
            }
            if (Region7(msg.x, msg.y))
            {
                DrawHook(0, 2 * PICHETGHT);
                arr[2][0] = { 1 };
            }
            if (Region8(msg.x, msg.y))
            {
                DrawHook(PICWIDTH, 2 * PICHETGHT);
                arr[2][1] = { 1 };
            }
            if (Region9(msg.x, msg.y))
            {
                DrawHook(2 * PICWIDTH, 2 * PICHETGHT);
                arr[2][2] = { 1 };
            }
            break;
            case WM_RBUTTONDOWN:
            if (Region1(msg.x, msg.y))
            {
                DrawFork(0, 0);
                arr[0][0] = { 0 };
            }
            if (Region2(msg.x, msg.y))
            {
                DrawFork(PICWIDTH, 0);
                arr[0][1] = { 0 };
            }
            if (Region3(msg.x, msg.y))
            {
                DrawFork(2 * PICWIDTH, 0);
                arr[0][2] = { 0 };
            }
            if (Region4(msg.x, msg.y))
            {
                DrawFork(0, PICHETGHT);
                arr[1][0] = { 0 };
            }
            if (Region5(msg.x, msg.y))
            {
                DrawFork(PICWIDTH, PICHETGHT);
                arr[1][1] = { 0 };
            }
            if (Region6(msg.x, msg.y))
            {
                DrawFork(2 * PICWIDTH, PICHETGHT);
                arr[1][2] = { 0 };
            }
            if (Region7(msg.x, msg.y))
            {
                DrawFork(0, 2 * PICHETGHT);
                arr[2][0] = { 0 };
            }
            if (Region8(msg.x, msg.y))
            {
                DrawFork(PICWIDTH, 2 * PICHETGHT);
                arr[2][1] = { 0 };
            }
            if (Region9(msg.x, msg.y))
            {
                DrawFork(2 * PICWIDTH, 2 * PICHETGHT);
                arr[2][2] = { 0 };
            }
            break;
        }
    }
}
void Judgement()
{
    int numy, numn;
    int result = -1;
    int i, j;
    for (i = 0; i < 3 && result==-1 ; i++)
    {
        numy = 0, numn = 0;
        for (j = 0; j < 3; j++)
        {
            if (arr[i][j] == 1)
            {
                numy++;
            }
            if(arr[i][j] == 0)
            {
                numn++;
            }
        }
        if (numy == 3)
        {
            result = 0;
        }
        if (numn == 3)
        {
            result = 1;
        }
    }
    if (result == -1)
    {
        for (j = 0; j < 3 && result == -1; j++)
        {
            numy = 0, numn = 0;
            for (i = 0; i < 3; i++)
            {
                if (arr[i][j] == 1)
                {
                    numy++;
                }
                if (arr[i][j] == 0)
                {
                    numn++;
                }
            }
            if (numy == 3)
            {
                result = 0;
            }
            if (numn == 3)
            {
                result = 1;
            }
        }
    }
    if (result == -1)
    {
        numy = numn = 0;
        for (i = 0; i < 3; i++)
        {
            if (arr[i][i] == 1)
            {
                numy++;
            }
            if (arr[i][i] == 0)
            {
                numn++;
            }
        }
        if (numy == 3)
        {
            result = 0;
        }
        if (numn == 3)
        {
            result = 1;
        }
    }
    if (result == -1)
    {
        numy = numn = 0;
        for (i = 0; i < 3; i++)
        {
            if(arr[i][3-i-1]==1)
            {
                numy++;
            }
            if (arr[i][3 - i - 1] == 0)
            {
                numn++;
            }
        }
        if (numy == 3)
        {
            result = 0;
        }
        if (numn == 3)
        {
            result = 1;
        }
    }
    if (result == -1)
    {
        if (arr[0][0] != -1 && arr[0][1] != -1 && arr[0][2] != -1
        && arr[1][0] != -1 && arr[1][1] != -1 && arr[1][2] != -1
        && arr[2][0] != -1 && arr[2][1] != -1 && arr[2][2] != -1)
        result = 2;
    }
    if (result == 0)
    {
        MessageBox(NULL, "勾赢,游戏结束", "游戏结束", MB_OKCANCEL);
        getchar();
    }
    if (result == 1)
    {
        MessageBox(NULL, "叉赢,游戏结束", "游戏结束", MB_OKCANCEL);
        getchar();
    }
    if (result == 2)
    {
        MessageBox(NULL, "平局,游戏结束", "游戏结束", MB_OKCANCEL);
        getchar();
    }
}
int main()
{
    InitDraw();
    while (true)
    {
        Play();
        Judgement();
    }
    getchar();
    closegraph();
    return 0;
}

release

easyx-image212

发布版 可直接发送给未装编译器的电脑

判断一个点是否在圆内

easyx-image213

判断欧几里得距离即可

#include<math.h>
pow(a,b);   a的b次方             sqrt(double c);   c的平方根

两圆相交范围判断

easyx-image214

附件

📄 C进阶 图形化 easyX.docx(原 word 笔记):C进阶 图形化 easyX.docx


⬅️ C语言与MySQL连接 🏠 00-编程语言 ➡️ 汇编